package server

import (
	"encoding/json"
	"fmt"
	"gitee.com/gitee-go/core"
	"gitee.com/gitee-go/core/common"
	"gitee.com/gitee-go/server/comm"
	"gopkg.in/alecthomas/kingpin.v2"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"
)

const Version = "0.1.0"

func Run() {
	app := kingpin.New("Gitee GO", "This is exec agent")
	regDaemon(app)
	kingpin.Version(Version)
	kingpin.MustParse(app.Parse(os.Args[1:]))
}

func regDaemon(app *kingpin.Application) {
	app.Flag("port", "http port").Int32Var(&comm.Port)
	app.Flag("debugs", "").Hidden().BoolVar(&comm.Debugs)
	app.Flag("debug", "").Hidden().BoolVar(&common.IsDebug)
	app.Command("run", "run the server").
		Default().
		Action(func(context *kingpin.ParseContext) error {
			err := comm.InitConf()
			go runWeb()
			if err != nil {
				println("start install:" + err.Error())
				installFun()
				err = comm.InitConf()
			}
			if err != nil {
				return err
			}
			core.InitLog(filepath.Join(comm.WorkPath, "logs"))
			return DaemonServer()
		})

	/*app.Command("install", "install the server daemon").
	Action(func(context *kingpin.ParseContext) error {
		return installFun()
	})*/
	app.Command("start", "start the server daemon").
		Action(func(context *kingpin.ParseContext) error {
			err := comm.InitConf()
			if err != nil {
				return err
			}
			core.InitLog(filepath.Join(comm.WorkPath, "logs"))
			return startFun()
		})

	app.Command("stop", "stop the server daemon").
		Action(func(context *kingpin.ParseContext) error {
			err := comm.InitConf()
			if err != nil {
				return err
			}
			core.InitLog(filepath.Join(comm.WorkPath, "logs"))
			return stopFun()
		})

	app.Command("status", "start the server daemon").
		Action(func(context *kingpin.ParseContext) error {
			err := comm.InitConf()
			if err != nil {
				return err
			}
			core.InitLog(filepath.Join(comm.WorkPath, "logs"))
			return statusFun()
		})

	app.Command("restart", "start the server daemon").
		Action(restartFun)

	// runner 必须
	cmd := app.Command("envs", "").
		Action(envsFun).Hidden()
	cmd.Arg("flpth", "config name(default:default)").
		Default().
		StringVar(&envsflpth)

}

var needStartCheck = true

func startFun() error {
	/*if needStartCheck {
		if err := statusFun(context); err == nil {
			println("the alias is running!!!!")
			return errors.New("the alias is running")
		}
	}*/
	fullpth, err := os.Executable()
	if err != nil {
		return err
	}
	pidpth := filepath.Join(comm.WorkPath, "server.pid")
	err = os.RemoveAll(pidpth)
	if err != nil {
		return err
	}
	pidfl, err := os.OpenFile(pidpth, os.O_CREATE|os.O_RDWR, 0644)
	if err != nil {
		return err
	}
	defer pidfl.Close()

	args := make([]string, 0)
	args = append(args, "run")
	if comm.Port > 0 {
		args = append(args, "--port")
		args = append(args, fmt.Sprintf("%d", comm.Port))
	}
	if common.IsDebug {
		args = append(args, "--debug")
	}

	println("start process")
	cmd := exec.Command(fullpth, args...)
	err = cmd.Start()
	if err != nil {
		return err
	}

	_, err = pidfl.WriteString(fmt.Sprintf("%d", cmd.Process.Pid))
	return err
}
func stopFun() error {
	pidpth := filepath.Join(comm.WorkPath, "server.pid")
	bts, err := ioutil.ReadFile(pidpth)
	if err != nil {
		return err
	}

	pid, err := strconv.Atoi(string(bts))
	if err != nil {
		return err
	}

	proc, err := os.FindProcess(pid)
	if err != nil {
		return err
	}
	println("first end process pid:", proc.Pid)
	err = proc.Signal(syscall.SIGINT)
	time.Sleep(time.Second)
	proc, err = os.FindProcess(pid)
	if err == nil {
		println("second end process pid:", proc.Pid)
		err = proc.Kill()
		time.Sleep(time.Second)
	}
	os.RemoveAll(pidpth)
	return nil
}
func statusFun() error {
	pidpth := filepath.Join(comm.WorkPath, "server.pid")
	bts, err := ioutil.ReadFile(pidpth)
	if err != nil {
		return err
	}

	pid, err := strconv.Atoi(string(bts))
	if err != nil {
		return err
	}

	proc, err := os.FindProcess(pid)
	if err != nil {
		return err
	}
	println("FindProcess pid:", proc.Pid)
	return nil
}
func restartFun(context *kingpin.ParseContext) error {
	needStartCheck = false
	err := comm.InitConf()
	if err != nil {
		return err
	}
	core.InitLog(filepath.Join(comm.WorkPath, "logs"))
	err = statusFun()
	if err == nil {
		err = stopFun()
		if err != nil {
			println("stop err:" + err.Error())
			return err
		}
	}
	return startFun()
}

var envsflpth string

func envsFun(context *kingpin.ParseContext) error {
	if envsflpth == "" {
		println("file path is empty")
		return nil
	}

	sysenvs := os.Environ()
	mapenvs := make(map[string]string)
	for _, v := range sysenvs {
		i := strings.Index(v, "=")
		if i > 0 {
			k := v[:i]
			mapenvs[k] = v[i+1:]
		}
	}
	bts, err := json.Marshal(mapenvs)
	if err != nil {
		println("get envs err:" + err.Error())
		return err
	}
	os.RemoveAll(envsflpth)
	ioutil.WriteFile(envsflpth, bts, 0640)
	return nil
}
